package direct_methods_for_systems_solver;

import matrix_operations.Matrix;
import matrix_operations.PartialPivotingWithScalar;
import matrix_operations.PartialPivoting;
import matrix_operations.Pivoting;

public abstract class GaussElimination {
	
	/**
	 * 
	 * @param A matrix of coefficients terms (A is a square matrix)
	 * @param B matrix of independents terms
	 * @return solution of system
	 */
	
	private static String msg = "";
	
	public static Matrix solveSystem(Matrix A, Matrix B){
		
		Matrix X;
		int n;
		
		int p; 		
		double m;
		double[] row;
		
		msg = "";

		
		if (!A.isSquare()){
			msg = "The coefficients matrix not is square";
			System.out.println(msg);
			return null;
		}
		
		n = A.getN();

		//Solution vector
		X = new Matrix(n, 1);
		
		//Gauss-Elimination
		for (int i = 0; i < n-1; i++) {
			
			if ((p = lessInteger(i, A)) < 0 ){
				msg = "Don't exist unique solution for this system!";
				System.out.println(msg);
				return null;
			}
			
			if (p != i){
				A.swapRows(p, i);
				B.swapRows(p, i);
			}
			
			for (int j = i + 1; j < n; j++) {
				m = A.getValue(j, i) / A.getValue(i, i);
							
				row = (A.getRow(j).subtract(A.getRow(i).scalarProduct(m))).getRowArray(0);
				A.changeRow(j, row);
				
				row = (B.getRow(j).subtract(B.getRow(i).scalarProduct(m))).getRowArray(0);
				B.changeRow(j, row);
			}				
		}				
		
		if (A.getValue(n-1, n-1) == 0){
			msg = "Don't exist unique solution for this system!";
			System.out.println(msg);
			return null;
		}
			
		//Start regressive replacment
		X.setValue(n-1, 0, B.getValue(n-1, 0) / A.getValue(n-1, n-1));
		
		for (int i = n-2; i >= 0; i--) {
			
			m = B.getValue(i, 0);
			for (int j = i+1; j < n; j++) {
				m-= A.getValue(i, j) * X.getValue(j, 0);
			}
			m /= A.getValue(i, i);
			
			//Set valur of X
			X.setValue(i, 0, m);
		}
		
		msg = "Procedure complete with sucess!";
		System.out.println(msg);
		return X;
	}
	
	public static Matrix solveSystemPartivalPivoting(Matrix A, Matrix B){
		return solveSystem(A, B, new PartialPivoting(A));
	}
	
	public static Matrix solveSystemPartivalPivotingWithScalar(Matrix A, Matrix B){
		return solveSystem(A, B, new PartialPivotingWithScalar(A));
	}
	
	
	private static Matrix solveSystem(Matrix A, Matrix B, Pivoting pivoting){
		Matrix X;
		int n;
		
		int p; 		
		double m;
		double[] row;

		
		if (!A.isSquare()){
			msg = "The coefficients matrix not is square";
			System.out.println(msg);
			return null;
		}
		
		n = A.getN();

		//Solution vector
		X = new Matrix(n, 1);
		
		//Gauss-Elimination with pivtoing
		for (int i = 0; i < n-1; i++) {
			
			
			//s[i] != 0
			if ((pivoting instanceof PartialPivotingWithScalar) && (!((PartialPivotingWithScalar)pivoting).existMaxNonNull())){
				msg = "Don't exist unique solution for this system!";
				System.out.println(msg);
				return null;
			}
				
			
			p = pivoting.lessInteger(i);
			
			if (pivoting.isSolube(p, i)){
				msg = "Don't exist unique solution for this system!";
				System.out.println(msg);
				return null;
			}
			
			if (p != i){
				A.swapRows(p, i);
				B.swapRows(p, i);
			}
			
			for (int j = i + 1; j < n; j++) {
				m = A.getValue(j, i) / A.getValue(i, i);
							
				row = (A.getRow(j).subtract(A.getRow(i).scalarProduct(m))).getRowArray(0);
				A.changeRow(j, row);
				
				row = (B.getRow(j).subtract(B.getRow(i).scalarProduct(m))).getRowArray(0);
				B.changeRow(j, row);
			}				
		}				
		
		if (A.getValue(n-1, n-1) == 0){
			msg = "Don't exist unique solution for this system!";
			System.out.println(msg);
			return null;
		}
			
		//Start regressive replacement
		X.setValue(n-1, 0, B.getValue(n-1, 0) / A.getValue(n-1, n-1));
		
		for (int i = n-2; i >= 0; i--) {
			
			m = B.getValue(i, 0);
			for (int j = i+1; j < n; j++) {
				m-= A.getValue(i, j) * X.getValue(j, 0);
			}
			m /= A.getValue(i, i);
			
			//Set valur of X
			X.setValue(i, 0, m);
		}
		
		msg = "Procedure complete with sucess!";
		System.out.println(msg);
		return X;
	}
	
	/**
	 * 
	 * @param i index
	 * @param A matrix of coefficients terms
	 * @return the less integer p such i <= p <= n with A[p][i] != 0
	 */
	
	private static int lessInteger(int i, Matrix A){
		int p = -1;
		
		
		for (int j = i; j < A.getN(); j++) {
			if (((j < p) || (p < 0)) && (A.getValue(j, i) != 0))
				p = j;
		}
		return p;
	}
	
	public static String getMsg(){
		return msg;
	}

}

